// Copyright (c) Microsoft Corporation. All rights reserved.

using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.TeamFoundation.Migration.Toolkit.VC;
using System.Data.SqlClient;
using System.Globalization;
using System.Data;
using System.Diagnostics;

namespace Microsoft.TeamFoundation.Migration.Toolkit.VC
{
    public class HighWaterMarkUpdatedEventArgs<T> : EventArgs
        where T : IConvertible
    {
        public HighWaterMarkUpdatedEventArgs(T previousValue, T newValue)
        {
            m_previous = previousValue;
            m_new = newValue;
        }

        public T Current
        {
            get
            {
                return m_previous;
            }
        }

        public T New
        {
            get
            {
                return m_new;
            }
        }

        T m_previous;
        T m_new;
    }

    [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1040:AvoidEmptyInterfaces")]
    public interface IHighWaterMark
    {
    }

    public class HighWaterMark<T> : IHighWaterMark
        where T : IConvertible
    {
        VersionControlSession m_session;
        string m_name;
        T m_current;

        public HighWaterMark(VersionControlSession session, string name)
            : this(session, name, default(T))
        {
        }

        public HighWaterMark(VersionControlSession session, string name, T defaultValue)
        {
            if (session == null)
            {
                throw new ArgumentNullException("session");
            }

            if (name == null)
            {
                throw new ArgumentNullException("name");
            }

            m_session = session;
            m_name = name;

            if (!tryGetLastAnalyzedTimeFromSessionVariable())
            {
                Value = defaultValue;
            }
        }

        public event EventHandler<HighWaterMarkUpdatedEventArgs<T>> BeforeUpdate;

        public virtual T Value
        {
            get
            {
                return m_current;
            }
            protected set
            {
                m_current = value;
            }
        }

        public virtual void Reload()
        {
            if (!tryGetLastAnalyzedTimeFromSessionVariable())
            {
                string settingsString;
                if (m_session.TryGetValue(m_name, out settingsString))
                {
                    Update(CreateValueFromString(settingsString));
                }
                else
                {
                    Update(Value);
                }
            }
        }

        public virtual void Update(T newValue)
        {
            if (newValue == null)
            {
                throw new ArgumentNullException("newValue");
            }

            OnBeforeUpdate(newValue);

            using (SqlConnection conn = DataAccessManager.Current.GetSqlConnection())
            {
                conn.Open();
                Update(newValue, conn);
            }
        }

        public virtual void Update(T newValue, SqlConnection conn)
        {
            if (conn == null)
            {
                throw new ArgumentNullException("conn");
            }

            Debug.Assert((conn.State & ConnectionState.Open) != 0,
                "The SQL connection must be open and valid");

            SessionVariables.SaveSessionVariable(
                m_session.Id,
                m_name,
                GetValueAsString(newValue), conn);

            m_current = newValue;
        }

        protected virtual string GetValueAsString(T value)
        {
            return value.ToString(CultureInfo.CurrentCulture);
        }

        protected virtual T CreateValueFromString(string str)
        {
            return (T)Convert.ChangeType(str, typeof(T), CultureInfo.CurrentCulture);
        }

        private void OnBeforeUpdate(T newValue)
        {
            if (BeforeUpdate != null)
            {
                BeforeUpdate(this, new HighWaterMarkUpdatedEventArgs<T>(m_current, newValue));
            }
        }

        private bool tryGetLastAnalyzedTimeFromSessionVariable()
        {
            string tempString = SessionVariables.GetSessionVariable(m_session.Id, m_name);

            if (tempString != null)
            {
                T temp = CreateValueFromString(tempString);
                if (temp != null)
                {
                    m_current = temp;
                    return true;
                }
            }

            return false;
        }

        public override string ToString()
        {
            return m_current.ToString(CultureInfo.CurrentCulture);
        }
    }
}
